#include "pch.hpp"
#include "timer.hpp"
#include "provider.hpp"

using namespace services;


const QString timer::SERVICE_NAME = "service::timer";




timer::timer_task::timer_task(timer_fn function, time_duration duration, ptime last)
	: m_function(function)
	, m_duration(duration)
	, m_last(last)
{}
///////////////////////////////////////////////////////////////////////////////////////////////////

bool timer::timer_task::should_notify() const
{
	return m_last + m_duration >= boost::posix_time::microsec_clock::local_time();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void timer::timer_task::operator()()
{
	m_function();
	m_last = boost::posix_time::microsec_clock::local_time();
}
///////////////////////////////////////////////////////////////////////////////////////////////////




timer::timer(boost::shared_ptr<const provider> p)
	: service(p)
	, m_timers(boost::shared_ptr<timers>(new timers()))
	, m_scheduler(p->service<scheduler>())
	, m_task(task::ptr(new task(boost::bind(&timer::task_run, this))))
{
	m_scheduler->add(m_task);
	// MUST NOT THROW AFTER HERE
}
///////////////////////////////////////////////////////////////////////////////////////////////////

timer::~timer()
{
	m_scheduler->remove(m_task);
}
///////////////////////////////////////////////////////////////////////////////////////////////////




QString timer::name() const
{
	return SERVICE_NAME;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

QStringList timer::dependencies() const
{
	return QStringList() << scheduler::SERVICE_NAME;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




timer::handle timer::add(timer_fn function, time_duration duration)
{
	handle h(new byte);
	m_timers.write()->insert(std::make_pair(h, timer_task(function, duration, boost::posix_time::microsec_clock::local_time())));
	return h;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void timer::remove(handle h)
{
	auto _timers = m_timers.write();

	auto it = _timers->begin();
	for(auto i = _timers->begin(); i != _timers->end(); ++i)
	{
		if(i->first == h)
		{
			it = i;
			break;
		}
	}

	if(it == _timers->end())
		BOOSTEXT_THROW(boostext::programming_error("There is no such timer"));

	_timers->erase(it);
}
///////////////////////////////////////////////////////////////////////////////////////////////////




void timer::task_run()
{
	auto _timers = m_timers.write();

	std::for_each(_timers->begin(), _timers->end(), [&](timers::reference t)
	{
		if(t.second.should_notify())
			t.second();
	});
}
///////////////////////////////////////////////////////////////////////////////////////////////////
